from gpiozero import  DigitalOutputDevice
from gpiozero.pins.pigpio import PiGPIOFactory
import time
import threading

def stepper_worker(stepper):
    delay = 0.001 # small delay,big speed
    while True:
        if stepper.status==Stepper_Motor.CLOCKWISE:
            for coil in [stepper.in4, stepper.in3,stepper.in2, stepper.in1]:
                coil.on()
                time.sleep(delay)
                coil.off()
        elif stepper.status==Stepper_Motor.ANTICLOCKWISE:
             for coil in [stepper.in1, stepper.in2,stepper.in3, stepper.in4]:
                coil.on()
                time.sleep(delay)
                coil.off()
        
             
class Stepper_Motor:
    STOP=0
    STEP_CLOCKWISE = 1
    STEP_ANTICLOCKWISE = 2
    CLOCKWISE = 3
    ANTICLOCKWISE = 4

    def __init__(self, IN1, IN2, IN3, IN4):
        self.DICT_CMD_ACTION = {Stepper_Motor.STEP_CLOCKWISE: self.step_clockwise,
                                Stepper_Motor.STEP_ANTICLOCKWISE: self.step_anticlockwise,
                                Stepper_Motor.STOP: self.stop,
                                Stepper_Motor.CLOCKWISE:  self.clockwise,
                                Stepper_Motor.ANTICLOCKWISE:  self.anticlockwise
                                }
        # init
        self.in1= DigitalOutputDevice(pin=IN1,pin_factory=PiGPIOFactory())
        self.in2= DigitalOutputDevice(pin=IN2,pin_factory=PiGPIOFactory())
        self.in3= DigitalOutputDevice(pin=IN3,pin_factory=PiGPIOFactory())
        self.in4= DigitalOutputDevice(pin=IN4,pin_factory=PiGPIOFactory())
         # Define constants
        self.DEG_PER_STEP = 0.5
        self.STEPS_PER_REVOLUTION = int(360 / self.DEG_PER_STEP)
        #
        self.status=Stepper_Motor.STOP
      
    # Function to move the stepper motor one step anticlockwise
    def step_anticlockwise(self, delay, steps):
        for _ in range(steps):
            for coil in [self.in1, self.in2, self.in3, self.in4]:
                coil.on()
                time.sleep(delay)
                coil.off()

    # Function to move the stepper motor one step clockwise
    def step_clockwise(self, delay, steps):
        for _ in range(steps):
            for coil in [self.in4, self.in3, self.in2, self.in1]:
                coil.on()
                time.sleep(delay)
                coil.off()

    def clockwise(self):
        self.status=Stepper_Motor.CLOCKWISE 

    def anticlockwise(self):
        self.status=Stepper_Motor.ANTICLOCKWISE     
     
    def stop(self):
        for coil in [self.in4, self.in3, self.in2, self.in1]:
            coil.off()
        self.status=Stepper_Motor.STOP

    def action(self, cmd_to_motor):
        delay = 0.00125
        steps = int(self.STEPS_PER_REVOLUTION/180)
        if cmd_to_motor==Stepper_Motor.STEP_CLOCKWISE  or cmd_to_motor==Stepper_Motor.STEP_ANTICLOCKWISE:
           self.status=cmd_to_motor
           self.DICT_CMD_ACTION[cmd_to_motor](delay, steps)
        else:
           self.DICT_CMD_ACTION[cmd_to_motor]()      

if __name__ == "__main__":
   stepper=Stepper_Motor(21, 20, 12,25)
   stepper.delay = 0.005
   stepper.action(Stepper_Motor.STEP_CLOCKWISE)
   time.sleep(1)
   stepper_thread = threading.Thread(target=stepper_worker, args=(stepper,))
   stepper_thread.start()
   stepper.action(Stepper_Motor.CLOCKWISE)
   time.sleep(10)
   stepper.action(Stepper_Motor.ANTICLOCKWISE)
   time.sleep(10)
   stepper.action(Stepper_Motor.STOP)
   